
#include <iostream>
#include <string>

#include "SDL.h"
#include "SDL_image.h"

#include "ddc_component.h"
#include "ddc_error.h"
#include "ddc_sprite.h"
#include "ddc_texture.h"
#include "ddc_time.h"

// DDC_SpriteTexture {

DDC_SpriteTexture *
    DDC_SpriteTexture::CutFromBaseTexture(DDC_BaseSDLTexture* texture, DDC_Rect *cutRect) {

    DDC_SpriteTexture *st = new DDC_SpriteTexture;

    st->cutRect.x = cutRect->x;
    st->cutRect.y = cutRect->y;
    st->cutRect.width = cutRect->width;
    st->cutRect.height = cutRect->height;

    st->texture = texture;

    return st;
}

DDC_SpriteTexture *
    DDC_SpriteTexture::CutFromBaseTexture(DDC_BaseSDLTexture* texture, DDC_Rect cutRect) {

    return DDC_SpriteTexture::CutFromBaseTexture(texture, &cutRect);
}

// } DDC_SpriteTexture

// DDC_SpriteRenderer {

void DDC_SpriteRenderer::__SetRenderer(SDL_Renderer *renderer) {
    this->renderer = renderer;
}

int DDC_SpriteRenderer::_RenderSubmit(DDC_SpriteTexture *texture,
                                      DDC_SpriteRenderContext *ctx) {
    SDL_Texture *baseTexture = texture->texture->baseTexture;

    SDL_Rect *srcRect = (SDL_Rect*)&texture->cutRect;
    SDL_Rect *dstRect = (SDL_Rect*)&ctx->spriteRect;

    float scale = (float)srcRect->h / srcRect->w;

    if (dstRect->h == 0 || dstRect->w == 0) {
        std::cerr << "Warning: size of dstRect contains 0\n";
    }

    if (dstRect->h / dstRect->w != scale) {
        if (dstRect->h > dstRect->w) {
            dstRect->w = dstRect->h * (1 / scale);
        } else {
            dstRect->h = dstRect->w * scale;
        }
    }

    if (ctx->rotationAngle == 0 && ctx->flip == DDC_SpriteFlip::FLIP_NONE) {
        return SDL_RenderCopy(renderer, baseTexture, srcRect, dstRect);
    }

    return SDL_RenderCopyEx(renderer, baseTexture, srcRect, dstRect,
                            ctx->rotationAngle, (SDL_Point*)&ctx->rotationCenter,
                            (SDL_RendererFlip)ctx->flip);
}

// } DDC_SpriteRenderer

// DDC_StaticSpriteRenderer {

void DDC_StaticSpriteRenderer::Render(DDC_SpriteRenderContext *ctx) {
    if (_RenderSubmit(spriteTexture, ctx) < 0) {
        std::cerr << "Warning: SDL_RenderCopy(Ex) failure, error = "
                  << SDL_GetError() << std::endl;
    }
}

void DDC_StaticSpriteRenderer::SetSpriteTexture(DDC_SpriteTexture *texture) {
    spriteTexture = texture;
}

// } DDC_StaticSpriteRenderer

// class DDC_SpriteAnimate {

DDC_SpriteTexture *DDC_SpriteAnimate::GetFrame() {
    int totalTime = _frames.size() * frameIntervalMS;

    if (timeAxis >= totalTime) {
        if (!loop)
            Stop();
        else
            Replay();
    }

    int index = timeAxis / frameIntervalMS;

    if (index >= _frames.size()) {
        if (stop) {
            index--;
        } else {
            std::cerr << "Warning: DDC_SpriteAnimate::GetFrame() index out of range."
                      << "(index = " << index << ", length = " << _frames.size() << ")"
                      << std::endl;
            return nullptr;
        }
    }

    DDC_SpriteTexture *st = _frames[index];

    if (stop)
        goto skipIncrement;

    timeAxis += DDC_Time::GetTimeDeltaMillisecond();

skipIncrement:
    return st;
}

// } DDC_SpriteAnimate

// class DDC_AnimateSpriteRenderer {

void DDC_AnimateSpriteRenderer::Render(DDC_SpriteRenderContext *ctx) {
    if (_currentState >= _animates.size() || _currentState < 0) {
        std::cerr << "Warning: DDC_AnimateSpriteRenderer::Render(): state out of range"
                  << std::endl;
        return;
    }
    DDC_SpriteAnimate *anim = _animates[_currentState];
    DDC_SpriteTexture *texture = anim->GetFrame();

    if (texture == nullptr) {
        std::cerr << "Warning: DDC_AnimateSpriteRenderer::Render(): texture not found\n";
        return;
    }

    _RenderSubmit(texture, ctx);
}

int DDC_AnimateSpriteRenderer::SetState(DDC_GameObject *go, int state) {
    DDC_Sprite2D *s2d = go->GetComponentByID<DDC_Sprite2D>(DDC_COMPONENT_SPRITE2D);
    DDC_AnimateSpriteRenderer *renderer = s2d->GetSpriteRender<DDC_AnimateSpriteRenderer>();

    if (renderer == nullptr) {
        _DDC_ErrSetString("Warning: DDC_AnimateSpriteRenderer::SetState()"
                          " DDC_AnimateSpriteRenderer not found.\n");
        return 1;
    }

    renderer->_currentState = state;

    return 0;
}

int DDC_AnimateSpriteRenderer::ReplayGameObject(DDC_GameObject *go, int state) {
    DDC_Sprite2D *s2d = go->GetComponentByID<DDC_Sprite2D>(DDC_COMPONENT_SPRITE2D);
    DDC_AnimateSpriteRenderer *renderer = s2d->GetSpriteRender<DDC_AnimateSpriteRenderer>();

    if (renderer == nullptr) {
        _DDC_ErrSetString("Warning: DDC_AnimateSpriteRenderer::StopGameObject()"
                          " DDC_AnimateSpriteRenderer not found.\n");
        return 1;
    }

    renderer->_animates[state < 0 ? renderer->_currentState : state]->Replay();

    return 0;
}

int DDC_AnimateSpriteRenderer::StopGameObject(DDC_GameObject *go, int state) {
    DDC_Sprite2D *s2d = go->GetComponentByID<DDC_Sprite2D>(DDC_COMPONENT_SPRITE2D);
    DDC_AnimateSpriteRenderer *renderer = s2d->GetSpriteRender<DDC_AnimateSpriteRenderer>();

    if (renderer == nullptr) {
        _DDC_ErrSetString("Warning: DDC_AnimateSpriteRenderer::StopGameObject()"
                          " DDC_AnimateSpriteRenderer not found.\n");
        return 1;
    }

    renderer->_animates[state < 0 ? renderer->_currentState : state]->Stop();

    return 0;
}

int DDC_AnimateSpriteRenderer::ResumeGameObject(DDC_GameObject *go, int state) {
    DDC_Sprite2D *s2d = go->GetComponentByID<DDC_Sprite2D>(DDC_COMPONENT_SPRITE2D);
    DDC_AnimateSpriteRenderer *renderer = s2d->GetSpriteRender<DDC_AnimateSpriteRenderer>();

    if (renderer == nullptr) {
        _DDC_ErrSetString("Warning: DDC_AnimateSpriteRenderer::ResumeGameObject()"
                          " DDC_AnimateSpriteRenderer not found.\n");
        return 1;
    }

    renderer->_animates[state < 0 ? renderer->_currentState : state]->Resume();

    return 0;
}
// } DDC_AnimateSpriteRenderer
